RStorage = game.ReplicatedStorage;
local sharedLibs = RStorage.SharedLibs;
local pp = require(sharedLibs.Prototype)
local co = require(sharedLibs.CoroLib)

TU = require(sharedLibs.Utils.TableUtil);
CU = require(sharedLibs.CoUtils);
Signal = require(sharedLibs.Signal.Signal);
local SignalExt = sharedLibs.Signal.SignalExtensions;
Signal:extends(SignalExt);
Debugger = require(sharedLibs.Debugger);

_IsStudio = Debugger.IsStudio;

Seq = require(sharedLibs.FP.Sequence);

--[[
    Dispatcher 会根据触发的Signal, 异步执行一个action (通过function构造出来的co)

    Dispatcher 永远不会停止, 所以Dispatch:Wait()后不应有其它代码.
    可以通过WaitOneOf等方法, 将Dispatch和其它Op配合, 来实现终止Dispatcher.
    例如: WaitOneOf(dispathOp, signal); --- signal触发时就终止dispatchOp

    BattleSimulator 游戏里的技能执行就是通过 Dispatch 实现的 (详见HeroObject和SkillCtrller)

    注: 这个AsyncOp没有继承AsyncOp基类, 而是通过内部封装一个coroutine实现的.
	这样做的好处是实现一个AsyncOp非常方便. 缺点是需要覆盖掉co的一些默认方法才能
	模拟成一个相对完善的AsyncOp(详见_overrideMethods).
]]
local cls = pp.proto();

function cls:constructor()
	--- 纪录所有当前正在运行的游离态Op
	self._ops = {};

	self._actions = {};

    --- 用来触发消息给 Dispatcher 对象
    self._signal = Signal.new();

	self._intervalCo = nil;
end

--- 可以不传递任何参数, 会直接调用defaultAction (CaseDefault)
--- 如果找不到对应eventType的aciton, 或没有指定defaultAction, 导致找不到有效
--- 的action, 就会抛出异常(release下只会打印warning)
function cls:Dispatch(eventType, ...)
	self._signal:Emit(eventType, ...);
end

--[[
	为指定事件类型绑定对应的异步操作. action必须是一个function.
	在事件触发时, 这个function会被转换为一个co. 
	这个co可以有参数. 在Dispatch(eventType, ...)后, 底层会把 "eventType, ..." 传递给这个co作为参数.
]]
function cls:Case(eventType, action)
	assert(typeof(action) == "function");
	self._actions[eventType] = action;
	return self;
end

--- 如果没有指定具体的case, 那么都会触发 defaultAction
function cls:CaseDefault(defaultAction)
	self._defAction = defaultAction;
end

function cls:GetAliveCount()
	return TU.GetTableSize(self._ops);
end

--- 等待一个Dispatcher. Dispatcher永远不会结束, 所以这个函数不会返回.
--- 上层需要通过WaitOneOf的方式来停止一个Dispatcher
function cls:Wait()
	local internalCo = co.New(function()
		while true do
			--- 这里是否有可能出现死循环? 例如, 某个Signal, 在一个for里连续Emit?
			local signalRet = self._signal:Wait(nil, true);
			local isVarRet = false;
			local evtType = nil;
			if typeof(signalRet) == "table" then
				isVarRet = true;
				evtType = signalRet[1];
			else
				--- Dispatch()时除了evtType外没有额外参数(或者连evtType都没有)
				evtType = signalRet;
			end

			--- 如果没有指定action, 直接用
			local action = self._actions[evtType] or self._defAction;

			if action then
				local actionCo = CU.ToOp(action);
				self._ops[actionCo] = actionCo;
	
				local endHandler = function()
					--- action结束后, 就从_ops离移除
					self._ops[actionCo] = nil;
				end

				--- 如果是一个coroutine, 就把Dispatch时传递的参数传递给coroutine
				if isVarRet then
					actionCo:SetParams2(signalRet);
				else
					actionCo:SetParams(signalRet);
				end
				actionCo:Start(endHandler, self._internalCo);
			else
				local msg = "Unhandled dispatcher event: "..tostring(evtType);
				error(msg);
			end
		end
	end)
	self._intervalCo = internalCo;

	internalCo:OnStop(function()
		--- 强制停止所有Op
		for _, op in pairs(self._ops) do
			op:Stop();
		end
		self._ops = {};
		self._intervalCo = nil;
	end)

	self:_overrideMethods(internalCo);
	
	--- 注: 这里永远不会结束!
	internalCo:Wait();
	return;
end

---
function cls:Start(continuation, parent)
	assert(false);
end

--- 覆盖掉internalCo的一些方法, 让他们调用时也会对_ops进行操作.
function cls:_overrideMethods(internalCo)
	--- 覆盖掉默认的 GetChildren, 返回 _ops
	internalCo.GetCurOps = function(self_)
		return Seq.fromTable(self._ops);
	end
	local oldPause = internalCo.Pause;
	internalCo.Pause = function(self_)
		for _, op in pairs(self._ops) do
			op:Pause();
		end
		oldPause(self_);
	end
	local oldResume = internalCo.Resume;
	internalCo.Resume = function(self_)
		for _, op in pairs(self._ops) do
			op:Resume();
		end
		oldResume(self_);
	end
end

return cls;
